﻿using System;
using System.Threading;

namespace NQueueing
{
    /// <summary>
    /// Базовый класс СМО с пакетной обработкой.
    /// </summary>
    /// <typeparam name="T">Тип элемента очереди.</typeparam>
    public abstract class BatchServerQueueBase<T> : ServerQueueBase<T>
    {
        protected int _minBatchSize;
        /// <summary>
        /// Минимальный размер пакета забираемый из очереди.
        /// </summary>
        public int MinBatchSize
        {
            get { return _minBatchSize; }
        }

        protected int _maxBatchSize;
        /// <summary>
        /// Максимальный размер пакета забираемый из очереди.
        /// </summary>
        public int MaxBatchSize
        {
            get { return _maxBatchSize; }
        }

        /// <summary>
        /// Конструктор.
        /// </summary>
        /// <param name="processingBatch">Ссылка на обработчик элементов очереди.</param>
        /// <param name="minBatchSize">Минимальный размер пакета забираемый из очереди.</param>
        /// <param name="maxBatchSize">Максимальный размер пакета забираемый из очереди.</param>
        /// <param name="maxQueueSize">Максимальный размер очереди.</param>
        /// <param name="maxThreadsCount">Максимальное число потоков обработки.</param>
        protected BatchServerQueueBase(int minBatchSize, int maxBatchSize, int maxQueueSize, int maxThreadsCount)
            : base(maxQueueSize, maxThreadsCount)
        {
            if (minBatchSize <= 0)
                throw new ArgumentOutOfRangeException("minBatchSize");
            _minBatchSize = minBatchSize;

            if (maxBatchSize <= 0)
                throw new ArgumentOutOfRangeException("maxBatchSize");
            _maxBatchSize = maxBatchSize;

            if (maxBatchSize < minBatchSize)
                throw new ArgumentException("maxBatchSize < minBatchSize");
        }

        protected abstract void ProcessingBatch(T[] items);

        protected override void Workflow()
        {
            while (true)
            {
                T[] items = null;
                lock (_syncQueue)
                {
                    if (_queue.Count >= _minBatchSize)
                    {
                        int count = Math.Min(_queue.Count, _maxBatchSize); // Не всю очередь, а только то что можем унести.
                        items = new T[count];
                        for (int i = 0; i < count; i++)
                        {
                            items[i] = _queue.Dequeue();
                        }
                    }
                }

                if (items != null)
                {
                    ProcessingBatch(items); // Работаем
                }
                else
                {
                    if (_shouldStop)
                    {
                        return; // Очередь заявок пуста и рабочий день окончен.
                    }
                    else
                    {
                        Thread.Sleep(_dequeueTimeout); // Рабочий день продожается.
                    }
                }
            }
        }
    }
}
